/***************************************************************************
 *   Copyright (C) 2007 by Ivan Vasić              *
 *   ivasic@gmail.com                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "bdecoder.h"
#include "bnode.h"
#include "bdictnode.h"
#include "blistnode.h"
#include "bvaluenode.h"
#include "decodingexception.h"

#include <QDebug>
#include <QtCore>

BDecoder::BDecoder(const QByteArray& data, quint32 offset)
		: QObject(0l), data(data), offset(offset)
{}


BDecoder::~BDecoder()

{}

BNode* BDecoder::decode()
{
	if (offset >= (quint32) data.size())
		return 0l;


	if (data[offset] == 'd')
	{
		BNode* n = 0l;

		try
		{
			n = parseDict();
			return n;
		}
		catch (DecodingException* ex)
		{
			if (n)
				delete n;

			throw new DecodingException(tr("Error parsing BDict"), Exception::Normal, __FILE__, __FUNCTION__, __LINE__, ex);
		}
	}

	if (data[offset] == 'l')
	{
		BNode* n = 0l;

		try
		{
			n = parseList();
			return n;
		}
		catch (DecodingException* ex)
		{
			if (n)
				delete n;

			throw new DecodingException(tr("Error parsing BList"), Exception::Normal, __FILE__, __FUNCTION__, __LINE__, ex);
		}
	}

	if (data[offset] == 'i')
	{

		BNode* n = 0l;

		try
		{
			n = parseInt();
			return n;
		}
		catch (DecodingException* ex)
		{
			if (n)
				delete n;

			throw new DecodingException(tr("Error parsing INT"), Exception::Normal, __FILE__, __FUNCTION__, __LINE__, ex);
		}
	}

	if (data[offset] >= '0' && data[offset] <= '9')
	{
		BNode* n = 0l;

		try
		{
			n = parseString();
			return n;
		}
		catch (DecodingException* ex)
		{
			if (n)
				delete n;

			throw new DecodingException(tr("Error parsing STRING"), Exception::Normal, __FILE__, __FUNCTION__, __LINE__, ex);
		}
	}

	///@TODO: Throw Exception
	throw new DecodingException(tr("Illegal token: %1").arg(data[offset]), Exception::Normal, __FILE__, __FUNCTION__, __LINE__);
}

BDictNode* BDecoder::parseDict()
{
	BDictNode* node = new BDictNode(offset);
	
	//preskoci 'd'
	++offset;

	while (data[offset] != 'e' && offset < (quint32) data.size())
	{
		//if (verbose) Out() << "Key : " << endl;
		BNode* k = decode();
		BValueNode* keynode = dynamic_cast<BValueNode*>(k);

		if (!keynode || keynode->type() != BValueNode::STRING) //BDict KEY MORA da bude tipa STRING!
		{
			delete k;
			throw new DecodingException(tr("BDict KEY must be type of STRING!"), Exception::Normal, __FILE__, __FUNCTION__, __LINE__);
		}

		BNode* val = decode();
		node->insert(keynode, val);		
	}

	//preskoci 'e'
	++offset;
	
	node->setLength(offset - node->offset());

	//if (verbose) Out() << "END" << endl;
	return node;
}

BListNode* BDecoder::parseList()
{
	BListNode* node = new BListNode(offset);
	
	//preskoci 'l'
	++offset;

	while (data[offset] != 'e' && offset <= (quint32) data.size())
	{
		BNode* n = decode();	
		node->append(n);
	}

	//preskoci 'e'
	++offset;
	
	node->setLength(offset - node->offset());
	
	//node->debug();

	return node;
}

BValueNode* BDecoder::parseString()
{
	quint32 offs = offset;

	QString num;

	//nadji string length ( 4:eggs )

	while (offset < (quint32) data.size() && data[offset] != ':')
	{
		num += data[offset];
		++offset;
	}

	//proveri da nismo stigli do kraja niza
	if (offset >= (quint32) data.size())
		throw new DecodingException(tr("Unexpected end of input."), Exception::Normal, __FILE__, __FUNCTION__, __LINE__);

	bool ok = true;

	int len = num.toInt(&ok); //konvertuj duzinu stringa u int

	if (!ok)//greska pri konvertovanju?!
		throw new DecodingException(tr("Error converting %1 to int").arg(num), Exception::Normal, __FILE__, __FUNCTION__, __LINE__);

	//preskoci ':'
	++offset;

	if (offset + len > (quint32) data.size()) //nedovrsen kod
		throw new DecodingException(tr("Unexpected end of input."), Exception::Normal, __FILE__, __FUNCTION__, __LINE__);

	QString value = data.mid(offset, len);

	//qDebug() << "Decode: " << data.mid(offs, offset - offs + len) << "=" << value;

	//podesi offset na sledeci node
	offset += len;
	
	BValueNode* node = new BValueNode(offs);
	node->setType(BValueNode::STRING);
	node->setValue(value);
	node->setLength(offset - offs);
	
	return node;
}

BValueNode* BDecoder::parseInt()
{
	//zapamti start offset
	quint32 offs = offset;
	
	//preskoci 'i'
	++offset;

	QString n;

	while (offset < (quint32) data.size() && data[offset] != 'e')
	{
		n += data[offset];
		++offset;
	}

	// proveri da nismo stigli do kraja
	if (offset >= (quint32) data.size())
		throw; //"Unexpected end of input"


	bool ok = true;

	int val = n.toInt(&ok);

	if (ok)
	{
		//preskoci 'e'
		++offset;
		
		BValueNode* vn = new BValueNode(offs, BValueNode::INT, val);
		vn->setLength(offset - offs);
		//if (verbose) Out() << "INT = " << val << endl;
		//qDebug() <<  "Dekodiro " << val;
		return vn;
	}
	else
	{
		qint64 bi = 0LL;
		bi = n.toLongLong(&ok);

		if (!ok)
			throw;//"Cannot convert %1 to an int")

		//preskoci 'e'
		++offset;
		
		BValueNode* vn = new BValueNode(offs, BValueNode::BIGINT, val);
		vn->setLength(offset - offs);
		//if (verbose) Out() << "INT64 = " << n << endl;
		//qDebug() <<  "Dekodiro " << val;

		return vn;
	}
}
